﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace CodeAnalysisRuleCollection.Common.CSharp
{
    /// <example>if (...) { ...; }</example>
    [ExportCodeRefactoringProvider("RemoveUnnecessaryBraces", LanguageNames.CSharp)]
    internal partial class RemoveUnnecessaryBracesCodeRefactoringProvider : ICodeRefactoringProvider
    {
        public async Task<IEnumerable<CodeAction>> GetRefactoringsAsync(Document document, TextSpan span, CancellationToken cancellationToken)
        {
            var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
            var ancestors = root.FindToken(span.Start).Parent.AncestorsAndSelf().ToList();

            BlockSyntax block = null;

            SyntaxNode current = ancestors.First();

            while (current != null)
            {
                if ((var statement = current as IfStatementSyntax) != null)
                {
                    block = statement.Statement as BlockSyntax;
                    break;
                }

                if ((var statement = current as ElseClauseSyntax) != null)
                {
                    block = statement.Statement as BlockSyntax;
                    break;
                }

                if ((var statement = current as ForEachStatementSyntax) != null)
                {
                    block = statement.Statement as BlockSyntax;
                    break;
                }

                if ((var statement = current as UsingStatementSyntax) != null)
                {
                    block = statement.Statement as BlockSyntax;
                    break;
                }

                if ((var statement = current as WhileStatementSyntax) != null)
                {
                    block = statement.Statement as BlockSyntax;
                    break;
                }

                if ((var statement = current as ForStatementSyntax) != null)
                {
                    block = statement.Statement as BlockSyntax;
                    break;
                }

                if ((var statement = current as LockStatementSyntax) != null)
                {
                    block = statement.Statement as BlockSyntax;
                    break;
                }

                current = current.Parent;
            }

            if (
                block != null &&
                (var single = block.GetEffectiveStatement()) != null &&
                !single.IsKind(SyntaxKind.LocalDeclarationStatement) && !single.IsKind(SyntaxKind.LabeledStatement)
            )
                return new[] { CodeAction.Create("Remove unnecessary braces", c => RemoveBlockAsync(root, document, block, c)) };

            return null;
        }

        private async Task<Document> RemoveBlockAsync(SyntaxNode root, Document document, BlockSyntax block, CancellationToken c)
        {
            return document.WithSyntaxRoot(
                root.ReplaceNode(
                    block,
                    block.Statements.Where(s => !s.IsKind(SyntaxKind.EmptyStatement)).Single()
                        .WithAdditionalAnnotations(Formatter.Annotation)
                )
            );
        }
    }

    internal static class BlockExtensions
    {
        public static StatementSyntax GetEffectiveStatement(this BlockSyntax block)
        {
            if (block == null)
                throw new ArgumentNullException("block");

            var statements = block.Statements.Where(s => !s.IsKind(SyntaxKind.EmptyStatement)).ToList();

            if (statements.Count != 1)
                return null;

            return statements.Single();
        }
    }
}
